8.3 - Example mod made with Harmony


This is the complete code of the TreeRegrowChanger mod - made by GLaD0S.
This mod changes the regrowth factor of trees.
You can find the complete code on github here.


using Endnight.Extensions;
using HarmonyLib;
using Microsoft.VisualBasic;
using RedLoader;
using Sons.Gameplay;
using Sons.Gameplay.Artifact;
using Sons.Gameplay.TreeCutting;
using Sons.TerrainDetail;
using SonsSdk;
using SUI;
using System;

namespace TreeRegrowChanger;

public class TreeRegrowChanger : SonsMod
{

    private static int EligibleTrees;
    public TreeRegrowChanger()
    {
        HarmonyPatchAll = true; //It is important to enable Harmony patches if you want to use them.
    }

    protected override void OnInitializeMod()
    {
        Config.Init();
    }

    protected override void OnSdkInitialized()
    {
        SettingsRegistry.CreateSettings(this, null, typeof(Config));
    }
    //In this method we both adjust the result, skip the original method and we call another method we patched.
    //We also never allow the original method to execute, this patch fully replaces the original method.
    [HarmonyPatch(typeof(WorldObjectLocatorManager), "CheckRegrowTrees")]
    private static class WorldObjectLocatorManagerCheckRegrowTreesPatch
    {
        //Here we reference all the original method arguments, so we can access and if necessary modify them.
        private static bool Prefix( ref Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppStructArray __result, 
                                                float regrowthFactor, 
                                                int minRegrowTreesPerCheck = -1, 
                                                int maxRegrowTreesPerCheck = -1, 
                                                bool force = false)
        {

        WorldObjectLocatorManager worldObjectLocatorManager;
        if (!WorldObjectLocatorManager.TryGetInstance(out worldObjectLocatorManager))
        {
            //The game expects an array of 2 integers, we return an empty array if we can't get the instance.
            __result = new int[2];
            return false; // Skip original method
        }

        if (!WorldObjectLocatorManager._treeRegrowth && !force)
        {
            __result = new int[2];
            return false; // Skip original method
        }
        //The regrow factor is a percentage, the game iterates over random eligible trees until the desired percentage is reached.
        //By defaul the game would always use 10 as the factor, but we can freely overwrite this value.
        regrowthFactor = Config.RegrowFactor.Value / 100f; 

        //Call the next method with our changed values
        __result = worldObjectLocatorManager.CheckRegrowTreesInternal(regrowthFactor, minRegrowTreesPerCheck, maxRegrowTreesPerCheck); 

        return false; // Skip original method
        }
    }

    //We use this method to call the message at the correct time, this makes sure that we already have all the required values we need.
    [HarmonyPatch(typeof(WorldObjectLocatorGroup), "TriggerRegrowth")] 
    private static class TriggerRegrowPatch
    {
        private static void Postfix(Il2CppSystem.Collections.Generic.List foundTreesToRegrow, 
                                                Il2CppSystem.Collections.Generic.List foundTreesToRegrowIndices,
                                                int countToRegrow)
        {
            if (Config.ShowMessage.Value)
            {
                //This will display a message in the bottom left corner, informing the user how many trees regrew.
                //Additionally we also display how many trees are eligible for regrowth.
                //Doing this gives the user additional feedback and makes it easier to confirm if the mod is working as expected.
                SonsTools.ShowMessage($"Regrown {countToRegrow} of {EligibleTrees} trees", 10f);   
            }
        }
    }
    //In this method we count the trees that are eligible for regrowth.
    [HarmonyPatch(typeof(WorldObjectLocatorManager), "CheckRegrowTreesInternal")] 
    private static class CheckRegrowTreesInternalPatch
    {
        //Here we fetch the instance of this specific WorldObjectLocatorManager, we then use it to fetch the states of the trees.
        private static void Prefix(WorldObjectLocatorManager __instance)
        {
            Il2CppSystem.Collections.Generic.IReadOnlyDictionary states = __instance._saveData.GetStates();
            Il2CppSystem.Collections.Generic.IReadOnlyDictionary unappliedStates = __instance._saveData.GetUnappliedStates();
            //We set up our own counter on how many trees are eligible for regrowth.
            int eligibleCount = 0;

            Action checkTreeState = delegate (WorldObjectState worldObjectState)
            {
                if (worldObjectState == WorldObjectState.Destroyed)
                {
                    eligibleCount++;
                }
            };

            states.Values.ForEach(checkTreeState);

            EligibleTrees = eligibleCount; //We store the count to use it later on in the displayed message.
            }
        }
    }